#include <assert.h>

#include "http.h"


HTTPHeaders newEmptyHeaders() {
    HTTPHeaders headers = {
        .data = NULL,
        .length = 0,
        .capacity = 0,
    };
    return headers;
}

void freeHeaders(HTTPHeaders *headers) {
    assert(headers != NULL);
    if (headers->data != NULL) {
        for (unsigned long i = 0; i < headers->length; i++) {
            const auto header = headers->data[i];
            free(header.key);
            free(header.value);
        }
        free(headers->data);
    }
    headers->data = NULL;
    headers->length = 0;
    headers->capacity = 0;
}

HTTPHeader getHeaderByIdx(const HTTPHeaders *headers, const unsigned long index) {
    assert(headers != NULL);
    assert(index < headers->length);
    return headers->data[index];
}

bool appendHeader(HTTPHeaders *headers, const HTTPHeader header) {
    assert(headers != NULL);
    assert(header.key != NULL);
    assert(header.value != NULL);
    if(headers->length == headers->capacity) {
        const unsigned long newCapacity = headers->capacity == 0? 4: 2 * headers->capacity;
        // printf("length: %zu, capacity: %zu, try to expand capacity to %zu\n", array->length, array->capacity, newCapacity);
        void *newData = realloc(headers->data, newCapacity * sizeof(HTTPHeader));
        if (newData == NULL) {
            // printf("length: %zu, capacity: %zu, failed to expand capcity to %zu\n", array->length, array->capacity, newCapacity);
            return false;
        }
        headers->data = newData;
        headers->capacity = newCapacity;
    }
    headers->data[headers->length].key = strdup(header.key);
    headers->data[headers->length].value = strdup(header.value);
    headers->length++;
    return true;
}

bool setHeader(HTTPHeaders *headers, const HTTPHeader header) {
    assert(headers != NULL);
    assert(header.key != NULL);
    assert(header.value != NULL);
    for(unsigned long i=0; i<headers->length; i++) {
        if (strcmp(headers->data[i].key, header.key) == 0) {
            free(headers->data[i].value);
            free(headers->data[i].key);
            headers->data[i].value = strdup(header.value);
            headers->data[i].key = strdup(header.key);
            return true;
        }
    }
    return appendHeader(headers, header);
}

bool removeHeaderByIdx(HTTPHeaders *headers, const unsigned long index) {
    assert(headers != NULL);
    if(index >= headers->length) return false;
    free(headers->data[index].key);
    free(headers->data[index].value);
    const unsigned long lastPos = headers->length - 2;
    for(unsigned long i=index; i<lastPos; i++) {
        headers->data[i] = headers->data[i+1];
    }
    headers->length--;
    headers->data[lastPos].key = NULL;
    headers->data[lastPos].value = NULL;
    return true;
}

char *getHeaderValueByKey(const HTTPHeaders *headers, const char *key) {
    assert(headers != NULL);
    assert(key != NULL);
    for(unsigned long i=0; i<headers->length; i++) {
        if (strcmp(headers->data[i].key, key) == 0) return headers->data[i].value;
    }
    return NULL;
}

bool removeHeaderByKey(HTTPHeaders *headers, const char *key) {
    assert(headers != NULL);
    unsigned long i;
    bool notfound = true;
    for(i=0; i<headers->length; i++) {
        if (strcmp(headers->data[i].key, key) == 0) {
            notfound = false;
            break;
        }
    }
    if (notfound) return false;
    return removeHeaderByIdx(headers, i);
}

unsigned long getContentLength(const HTTPHeaders *headers) {
    const auto lengthStr = getHeaderValueByKey(headers, "Content-Length");
    if(lengthStr == NULL) return 0;
    return strtoul(lengthStr, NULL, 10);
}

bool setContentLength(HTTPHeaders *headers, const unsigned long length) {
    char lengthStr[32];
    snprintf(lengthStr, 32, "%lu", length);
    const HTTPHeader header = {
        .key = "Content-Length",
        .value = lengthStr,
    };
    return setHeader(headers, header);
}

char *getContentType(const HTTPHeaders *headers) {
    return getHeaderValueByKey(headers, "Content-Type");
}

bool setContentType(HTTPHeaders *headers, char *type) {
    const HTTPHeader header = {
        .key = "Content-Type",
        .value = type,
    };
    return setHeader(headers, header);
}
